﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace TeF.Runner.Helpers
{
    internal class TestParentHelper<TTestEntityParent, TTestEntityChild> : IDisposable
        where TTestEntityParent : class, ITestEntityParentOfT<TTestEntityParent, TTestEntityChild>
        where TTestEntityChild : class, ITestEntityChildOfT<TTestEntityParent, TTestEntityChild>
    {
        private readonly ISet<TTestEntityChild> _children = new HashSet<TTestEntityChild>();

        public TestParentHelper(TTestEntityParent parent)
        {
            if (parent == null)
                throw new ArgumentNullException("parent");

            Parent = parent;
        }

        public TTestEntityParent Parent { get; private set; }

        public IEnumerable<TTestEntityChild> Children
        {
            get
            {
                return
                    _children
                        .OrderBy(n => n.Name)
                        .ToArray();
            }
        }
        
        public void AddChild(TTestEntityChild child)
        {
            if (child == null)
                throw new ArgumentNullException("child");

            if (_children.Contains(child))
                throw new ArgumentException("child");

            child.StateChanged += Child_StateChanged;

            _children.Add(child);
        }

        public void Dispose()
        {
            foreach (var child in _children)
            {
                child.StateChanged -= Child_StateChanged;
                
                var disposable = child as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }
        }

        private void Child_StateChanged(object sender, StateChangedEventArgs args)
        {
            if (Parent.State == TestState.Skipped)
                return;

            if (_children.All(c => c.State == TestState.Skipped))
            {
                Parent.SetState(TestState.Skipped);
                return;
            }

            if (_children.Any(c => c.State == TestState.Failed))
            {
                Parent.SetState(TestState.Failed);
                return;
            }

            if (_children.All(c => new[] { TestState.Passed, TestState.Skipped }.Contains(c.State)))
            {
                Parent.SetState(TestState.Passed);
                return;
            }

            Parent.SetState(TestState.Loaded);
        }
    }
}
